// --- Copyright (c) 2006-2008 Stefan Kyntchev ---
// This software is written and copyrighted by Stefan Kyntchev 
// and BeyondPod Team members. All rights are reserved.
// Author contact: support@beyondpod.mobi
// ------------------------------------------------------------------------
// This file is part of BeyondPod RSS Feed Reader and Podcast manager
// (www.codeplex.com/BeyondPod) 
// BeyondPod is free software: you can redistribute it and/or modify 
// it under the terms of the GNU General Public License as published by 
// the Free Software Foundation, either version 3 of the License, or 
// (at your option) any later version. 
//  
// BeyondPod is distributed in the hope that it will be useful, 
// but WITHOUT ANY WARRANTY; without even the implied warranty of 
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
// GNU General Public License for more details. 
//  
// You should have received a copy of the GNU General Public License
// along with BeyondPod. If not, see <http://www.gnu.org/licenses/>
// ------------------------------------------------------------------------
// COPYRIGHT NOTICE: This copyright notice may NOT be removed, obscured or modified 
// without written consent from the author.
// --- End copyright notice --- 


using System;
using System.Xml;
using svs.Mobile.BeyondMedia.FeedCore.RSS.Aggregators;
using svs.Mobile.BeyondMedia.FeedCore.RSS.Entities;

namespace svs.Mobile.BeyondMedia.FeedCore.RSS.Parsers
{
	public class RssFeedParser : ParserBase
	{
		public enum FeedParseLevel
		{
			Complete,
			ItemAndEnclosureEssentials,
			ItemEssentials
		}

		private readonly string m_EnclosureDownloadPath;
		private readonly bool m_ForceUniqueEnclosureNames;
		private readonly RssFeed m_Feed = new RssFeed();
		private bool m_RSSTagFound = false;
		private readonly RssFeedCache.LoadFilterDelegate m_shouldStop;
		private FeedParseLevel m_ParseLevel = FeedParseLevel.Complete;

		public RssFeedParser(string i_EnclosureDownloadPath, bool i_forceUniqueNames, Guid feedID, RssFeedCache.LoadFilterDelegate i_shouldStop, FeedParseLevel parseLevel)
		{
			//RSS Tags
			m_handlers["rss"] = RssHandler;
			m_handlers["title"] = delegate(XmlReader aReader) { m_Feed.Title = ReadElementString(aReader); };
			m_handlers["link"] = delegate(XmlReader aReader) { m_Feed.Link = ReadElementString(aReader); };
			m_handlers["description"] = delegate(XmlReader aReader) { m_Feed.Description = ReadElementString(aReader); };
			m_handlers["lastBuildDate"] = PubDateHandler;
			m_handlers["pubDate"] = PubDateHandler;
			m_handlers["item"] = ItemHandler;
			m_handlers["image"] = ImageHandler;
			m_handlers["aggregateFeedInfo"] = AggregateInfoHandler;

			//Atom tags
			m_handlers["updated"] = PubDateHandler;
			m_handlers["entry"] = AtomEntryHandler;

			//Opml handler
			m_handlers["opml"] = OpmlHandler;

			m_EnclosureDownloadPath = i_EnclosureDownloadPath;
			m_ForceUniqueEnclosureNames = i_forceUniqueNames;
			m_shouldStop = i_shouldStop;
			m_ParseLevel = parseLevel;
			m_Feed.FeedID = feedID;
		}

		#region RSS Handlers

		private void OpmlHandler(XmlReader aReader)
		{
			//If we find opml tag before we find rss tag - this is an opml feed
			if (!m_RSSTagFound)
				throw new CoreHelper.EncounteredOpmlFeedException();
		}

		private void ItemHandler(XmlReader aReader)
		{
			RssFeedItemParser ip = new RssFeedItemParser(m_EnclosureDownloadPath, m_ForceUniqueEnclosureNames, m_ParseLevel, m_Feed);
			ip.Handle(aReader);
			m_Feed.Items.Add(ip.Item);
		}

		private void RssHandler(XmlReader aReader)
		{
			AttributeCollection att = ReadAttributes(aReader);
			if (!att.ContainsKey("version") || string.IsNullOrEmpty(att["version"]))
				throw new InvalidOperationException("The feed is not RSS 2.0 Feed!");
			else
				m_RSSTagFound = true;
		}

		private void PubDateHandler(XmlReader aReader)
		{
			if (!m_Feed.PubDate.HasValue)
			{
				string orgDate;
				m_Feed.PubDate = ReadDateTime(aReader, out orgDate);
			}
		}

		private void AggregateInfoHandler(XmlReader aReader)
		{
			var aggregaterUri = ReadElementString(aReader);

			try
			{
				m_Feed.AggregateInfo = new CategoryAggregate(new Uri(aggregaterUri));
			}
			catch (Exception)
			{
				CoreHelper.WriteLogEntry("Failed to parse aggreage info " + aggregaterUri);
				m_Feed.AggregateInfo = null;
			}
		}

		private void ImageHandler(XmlReader aReader)
		{
			if (aReader.Prefix == RssFeed.IMG_ITUNES_KEY)
			{
				AttributeCollection att = ReadAttributes(aReader);
				if (att.ContainsKey("href") && !string.IsNullOrEmpty(att["href"]))
					m_Feed.FeedImageLinks[RssFeed.IMG_ITUNES_KEY] = att["href"];
			}
			else
			{
				RssFeedImageParser ip = new RssFeedImageParser();
				ip.Handle(aReader);
				m_Feed.FeedImageLinks[RssFeed.IMG_RSS_KEY] = ip.ImageUrl;
			}
		}

		#endregion

		#region Atom Handlers

		private void AtomEntryHandler(XmlReader aReader)
		{
			var ip = new AtomEntryParser(m_EnclosureDownloadPath, m_ForceUniqueEnclosureNames, m_Feed, m_ParseLevel);
			ip.Handle(aReader);
			m_Feed.Items.Add(ip.Item);
		}

		#endregion

		public RssFeed Feed
		{
			get { return m_Feed; }
		}

		protected override bool ContinueParsing()
		{
			if (m_shouldStop != null)
				return m_shouldStop(Feed);
			else
				return true;
		}
	}
}